package com.yfunc.common.event.executor;

import java.util.Collections;
import java.util.HashSet;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;

import com.yfunc.common.event.Event;
import com.yfunc.common.event.EventExecutor;
import com.yfunc.common.event.EventHandler;
import com.yfunc.common.event.handlers.AnnotationEventHandler;

public class AsynchronousEventExecutor implements EventExecutor {

	private Set<EventHandler> handles;
	private Queue<Event> events;
	private Queue<Event> executorEvents;
	private ExecutorService executorService;
	private volatile boolean runable;

	private long timeMillis;

	public AsynchronousEventExecutor() {
		this(Executors.newFixedThreadPool(10));
	}

	public AsynchronousEventExecutor(ExecutorService executorService) {
		this.executorService = executorService;
		this.handles = Collections.synchronizedSet(new HashSet<EventHandler>());
		this.events = new LinkedBlockingDeque<Event>();
		this.executorEvents = new LinkedBlockingDeque<Event>();
		this.runable = true;
		this.start();
	}

	@Override
	public void publish(Event event) {
		if (event == null) {
			throw new IllegalArgumentException("published null event!");
		}
		this.events.add(event);
	}

	@Override
	public void register(EventHandler handler) {
		if (handler == null) {
			throw new IllegalArgumentException("register null handler!");
		}
		this.handles.add(handler);
	}

	@Override
	public void register(Object handler) {
		if (handler == null) {
			throw new IllegalArgumentException("register null handler!");
		}
		this.register(new AnnotationEventHandler(handler));
	}

	private void start() {
		this.executorService.submit(new DaemonRunnable());
		this.timeMillis = System.currentTimeMillis();
	}

	public void stop() {
		this.runable = false;
		this.executorService.shutdown();
		System.out.println((System.currentTimeMillis() - timeMillis) / 1000);
	}

	private synchronized void synchronizedEvents() {
		if (events.size() > 0) {
			executorEvents.addAll(events);
			events.clear();
		}
	}

	class DaemonRunnable implements Runnable {
		public void run() {
			while (runable) {
				synchronizedEvents();
				if (handles.size() > 0) {
					while (executorEvents.size() > 0) {
						try {
							EventHandler[] eventHanlers = handles
									.toArray(new EventHandler[0]);
							Event event = executorEvents.remove();
							for (EventHandler handler : eventHanlers) {
								if (handler.isSupported(event)) {
//									handler.handle(event);
									executorService.submit(new HandlerRunnable(handler, event));
								}
							}
						} catch (Exception e) {
							// todo log
						}
					}
				}
			}
		}
	}

	class HandlerRunnable implements Runnable {
		private EventHandler handler;
		private Event event;

		public HandlerRunnable(EventHandler handler, Event event) {
			this.handler = handler;
			this.event = event;
		}

		@Override
		public void run() {
			if (handler != null && event != null) {
				handler.handle(event);
			}
		}
	}
}
